<!doctype html>







<html
  class="not-ready lg:text-base"
  style="--bg:#faf8f1"
  lang="en-us"
  dir="ltr"
><head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta
    name="viewport"
    content="width=device-width, initial-scale=1, shrink-to-fit=no"
  />

  
  <title>Postgresql 源码研究之基础备份工具pg_basebackup - 数据库的路上</title>

  
  <meta name="theme-color" />

  <meta name="description" content="pg_basebackup介绍
pg_basebackup 是 PostgreSQL 内置的 物理备份工具，用于创建数据库集群的 一致基础备份。它支持 在线热备份（备份时数据库可正常读写），是实现高可用性（如流复制）和灾难恢复的核心工具之一
main函数介绍
根据代码分析，main()函数在pg_basebackup.c中主要完成了以下逻辑步骤：


初始化处理：

解析命令行参数
设置信号处理
初始化全局变量和数据结构



连接数据库：


建立与PostgreSQL服务器的连接
conn = GetConnection();


验证服务器版本兼容性




备份准备：


创建或清理目标目录
verify_dir_is_empty_or_create(basedir, &amp;made_new_pgdata, &amp;found_existing_pgdata);


处理表空间映射


设置WAL日志目录




执行备份：

根据参数选择备份格式(plain/tar)
启动后台WAL流接收进程
通过流复制协议接收基础备份数据

关键函数调
BaseBackup() - 核心备份逻辑
StartLogStreamer()` - 启动WAL流接收
cleanup_directories_atexit()` - 退出时清理


全量备份
不指定增量备份情况下,默认是全量备份.
主要步骤
client 端 BaseBackup();
▶  通过libpq 发送&quot;BASE_BACKUP&quot; 命令给server

▶   对于采用流复制备份单独启动一个进程接收wal日志（数据库在备份期间还存在并发写入的情况）	
StartLogStreamer(xlogstart..)

会使用临时物理复制槽位，保证wal在备份期间不丢失

zhou=# select slot_name from pg_replication_slots ;
    slot_name      
---------------------
pg_basebackup_19476
(1 row)
▶ 循环接收服务端发送过来的备份数据直到结束
ReceiveArchiveStream(conn, client_compress);" />
  <meta name="author" content="数据库的路上" /><link rel="preload stylesheet" as="style" href="https://gitcode.com/maozicb/database_doc/main.min.css" />

  
  <link rel="preload" as="image" href="https://gitcode.com/maozicb/database_doc/theme.png" />

  

  <link rel="preload" as="image" href="https://gitcode.com/maozicb/database_doc/github.svg" />

  <script
    defer
    src="https://gitcode.com/maozicb/database_doc/highlight.min.js"
    onload="hljs.initHighlightingOnLoad();"
  ></script>

  
  <link
    rel="icon"
    href="https://gitcode.com/maozicb/database_doc/favicon.ico"
  />
  <link
    rel="apple-touch-icon"
    href="https://gitcode.com/maozicb/database_doc/apple-touch-icon.png"
  />

  <meta name="generator" content="Hugo 0.147.7">
  <meta itemprop="name" content="Postgresql 源码研究之基础备份工具pg_basebackup">
  <meta itemprop="description" content="pg_basebackup介绍 pg_basebackup 是 PostgreSQL 内置的 物理备份工具，用于创建数据库集群的 一致基础备份。它支持 在线热备份（备份时数据库可正常读写），是实现高可用性（如流复制）和灾难恢复的核心工具之一
main函数介绍 根据代码分析，main()函数在pg_basebackup.c中主要完成了以下逻辑步骤：
初始化处理：
解析命令行参数 设置信号处理 初始化全局变量和数据结构 连接数据库：
建立与PostgreSQL服务器的连接
conn = GetConnection(); 验证服务器版本兼容性
备份准备：
创建或清理目标目录
verify_dir_is_empty_or_create(basedir, &amp;made_new_pgdata, &amp;found_existing_pgdata); 处理表空间映射
设置WAL日志目录
执行备份：
根据参数选择备份格式(plain/tar) 启动后台WAL流接收进程 通过流复制协议接收基础备份数据 关键函数调
BaseBackup() - 核心备份逻辑
StartLogStreamer()` - 启动WAL流接收
cleanup_directories_atexit()` - 退出时清理
全量备份 不指定增量备份情况下,默认是全量备份.
主要步骤 client 端 BaseBackup(); ▶ 通过libpq 发送&#34;BASE_BACKUP&#34; 命令给server
▶ 对于采用流复制备份单独启动一个进程接收wal日志（数据库在备份期间还存在并发写入的情况）	StartLogStreamer(xlogstart..)
会使用临时物理复制槽位，保证wal在备份期间不丢失 zhou=# select slot_name from pg_replication_slots ; slot_name --------------------- pg_basebackup_19476 (1 row) ▶ 循环接收服务端发送过来的备份数据直到结束 ReceiveArchiveStream(conn, client_compress);">
  <meta itemprop="datePublished" content="2025-05-16T20:44:59+08:00">
  <meta itemprop="dateModified" content="2025-05-16T20:44:59+08:00">
  <meta itemprop="wordCount" content="165">
  <meta itemprop="keywords" content="PostgreSql"><meta property="og:url" content="https://gitcode.com/maozicb/database_doc/posts/db/postgresql/postgresql-%E6%BA%90%E7%A0%81%E7%A0%94%E7%A9%B6%E4%B9%8B%E5%9F%BA%E7%A1%80%E5%A4%87%E4%BB%BD%E5%B7%A5%E5%85%B7pg_basebackup/">
  <meta property="og:site_name" content="数据库的路上">
  <meta property="og:title" content="Postgresql 源码研究之基础备份工具pg_basebackup">
  <meta property="og:description" content="pg_basebackup介绍 pg_basebackup 是 PostgreSQL 内置的 物理备份工具，用于创建数据库集群的 一致基础备份。它支持 在线热备份（备份时数据库可正常读写），是实现高可用性（如流复制）和灾难恢复的核心工具之一
main函数介绍 根据代码分析，main()函数在pg_basebackup.c中主要完成了以下逻辑步骤：
初始化处理：
解析命令行参数 设置信号处理 初始化全局变量和数据结构 连接数据库：
建立与PostgreSQL服务器的连接
conn = GetConnection(); 验证服务器版本兼容性
备份准备：
创建或清理目标目录
verify_dir_is_empty_or_create(basedir, &amp;made_new_pgdata, &amp;found_existing_pgdata); 处理表空间映射
设置WAL日志目录
执行备份：
根据参数选择备份格式(plain/tar) 启动后台WAL流接收进程 通过流复制协议接收基础备份数据 关键函数调
BaseBackup() - 核心备份逻辑
StartLogStreamer()` - 启动WAL流接收
cleanup_directories_atexit()` - 退出时清理
全量备份 不指定增量备份情况下,默认是全量备份.
主要步骤 client 端 BaseBackup(); ▶ 通过libpq 发送&#34;BASE_BACKUP&#34; 命令给server
▶ 对于采用流复制备份单独启动一个进程接收wal日志（数据库在备份期间还存在并发写入的情况）	StartLogStreamer(xlogstart..)
会使用临时物理复制槽位，保证wal在备份期间不丢失 zhou=# select slot_name from pg_replication_slots ; slot_name --------------------- pg_basebackup_19476 (1 row) ▶ 循环接收服务端发送过来的备份数据直到结束 ReceiveArchiveStream(conn, client_compress);">
  <meta property="og:locale" content="en_us">
  <meta property="og:type" content="article">
    <meta property="article:section" content="posts">
    <meta property="article:published_time" content="2025-05-16T20:44:59+08:00">
    <meta property="article:modified_time" content="2025-05-16T20:44:59+08:00">

  <meta name="twitter:card" content="summary">
  <meta name="twitter:title" content="Postgresql 源码研究之基础备份工具pg_basebackup">
  <meta name="twitter:description" content="pg_basebackup介绍 pg_basebackup 是 PostgreSQL 内置的 物理备份工具，用于创建数据库集群的 一致基础备份。它支持 在线热备份（备份时数据库可正常读写），是实现高可用性（如流复制）和灾难恢复的核心工具之一
main函数介绍 根据代码分析，main()函数在pg_basebackup.c中主要完成了以下逻辑步骤：
初始化处理：
解析命令行参数 设置信号处理 初始化全局变量和数据结构 连接数据库：
建立与PostgreSQL服务器的连接
conn = GetConnection(); 验证服务器版本兼容性
备份准备：
创建或清理目标目录
verify_dir_is_empty_or_create(basedir, &amp;made_new_pgdata, &amp;found_existing_pgdata); 处理表空间映射
设置WAL日志目录
执行备份：
根据参数选择备份格式(plain/tar) 启动后台WAL流接收进程 通过流复制协议接收基础备份数据 关键函数调
BaseBackup() - 核心备份逻辑
StartLogStreamer()` - 启动WAL流接收
cleanup_directories_atexit()` - 退出时清理
全量备份 不指定增量备份情况下,默认是全量备份.
主要步骤 client 端 BaseBackup(); ▶ 通过libpq 发送&#34;BASE_BACKUP&#34; 命令给server
▶ 对于采用流复制备份单独启动一个进程接收wal日志（数据库在备份期间还存在并发写入的情况）	StartLogStreamer(xlogstart..)
会使用临时物理复制槽位，保证wal在备份期间不丢失 zhou=# select slot_name from pg_replication_slots ; slot_name --------------------- pg_basebackup_19476 (1 row) ▶ 循环接收服务端发送过来的备份数据直到结束 ReceiveArchiveStream(conn, client_compress);">

  <link rel="canonical" href="https://gitcode.com/maozicb/database_doc/posts/db/postgresql/postgresql-%E6%BA%90%E7%A0%81%E7%A0%94%E7%A9%B6%E4%B9%8B%E5%9F%BA%E7%A1%80%E5%A4%87%E4%BB%BD%E5%B7%A5%E5%85%B7pg_basebackup/" />
</head>
<body
    class="bg-(--bg) text-black antialiased duration-200 ease-out [-webkit-tap-highlight-color:transparent] dark:text-white"
  ><header
  class="mx-auto flex h-[4.5rem] max-w-(--w) px-8 whitespace-nowrap lg:justify-center"
>
  <div class="relative z-50 flex items-center ltr:mr-auto rtl:ml-auto">
    <a
      class="-translate-y-[1px] text-2xl font-medium"
      href="https://gitcode.com/maozicb/database_doc/"
      >数据库的路上</a
    >
    <div
      class="btn-dark text-[0px] ltr:ml-4 rtl:mr-4 h-6 w-6 shrink-0 cursor-pointer [background:url(./theme.png)_left_center/_auto_theme('spacing.6')_no-repeat] [transition:_background-position_0.4s_steps(5)] dark:[background-position:right]"
      role="button"
      aria-label="Dark"
    ></div>
  </div>

  <div
    class="btn-menu relative z-50 flex h-[4.5rem] w-[5rem] shrink-0 cursor-pointer flex-col items-center justify-center gap-2.5 lg:hidden ltr:-mr-8 rtl:-ml-8"
    role="button"
    aria-label="Menu"
  ></div>

  <script>
    
    const htmlClass = document.documentElement.classList;
    setTimeout(() => {
      htmlClass.remove('not-ready');
    }, 10);

    
    const btnMenu = document.querySelector('.btn-menu');
    btnMenu.addEventListener('click', () => {
      htmlClass.toggle('open');
    });

    
    const metaTheme = document.querySelector('meta[name="theme-color"]');
    const lightBg = '#faf8f1'.replace(/"/g, '');
    const setDark = (isDark) => {
      metaTheme.setAttribute('content', isDark ? '#000' : lightBg);
      htmlClass[isDark ? 'add' : 'remove']('dark');
      localStorage.setItem('dark', isDark);
    };

    
    const darkScheme = window.matchMedia('(prefers-color-scheme: dark)');
    if (htmlClass.contains('dark')) {
      setDark(true);
    } else {
      const darkVal = localStorage.getItem('dark');
      setDark(darkVal ? darkVal === 'true' : darkScheme.matches);
    }

    
    darkScheme.addEventListener('change', (event) => {
      setDark(event.matches);
    });

    
    const btnDark = document.querySelector('.btn-dark');
    btnDark.addEventListener('click', () => {
      setDark(localStorage.getItem('dark') !== 'true');
    });
  </script>

  <div
    class="nav-wrapper fixed inset-x-0 top-full z-40 flex h-full flex-col justify-center bg-(--bg) pb-16 duration-200 select-none lg:static lg:h-auto lg:flex-row lg:bg-transparent! lg:pb-0 lg:transition-none"
  ><nav
      class="lg:ml-12 lg:flex lg:flex-row lg:items-center lg:space-x-10 rtl:space-x-reverse"
    ><a
        class="block text-center text-xl leading-[5rem] lg:text-base lg:font-normal"
        href="/maozicb/database_doc/about/"
        >About</a
      ><a
        class="block text-center text-xl leading-[5rem] lg:text-base lg:font-normal"
        href="/maozicb/database_doc/contact/"
        >Contact</a
      ></nav><nav
      class="mt-12 flex justify-center space-x-10 lg:mt-0 lg:items-center ltr:lg:ml-14 rtl:space-x-reverse rtl:lg:mr-14 dark:invert"
    >
      <a
        class="h-7 w-7 text-[0px] [background:var(--url)_center_center/cover_no-repeat] lg:h-6 lg:w-6"
        style="--url: url(./github.svg)"
        href="https://github.com/newusername"
        target="_blank"
        rel="me"
      >github</a>
    </nav>
  </div>
</header>
<main
      class="prose prose-neutral dark:prose-invert relative mx-auto min-h-[calc(100vh-9rem)] max-w-(--w) px-8 pt-14 pb-16"
    ><article>
  <header class="mb-14">
    <h1 class="my-0! pb-2.5">Postgresql 源码研究之基础备份工具pg_basebackup</h1><div class="text-xs antialiased opacity-60"><time>May 16, 2025</time></div></header>

  <section><h2 id="pg_basebackup介绍">pg_basebackup介绍</h2>
<p>pg_basebackup 是 PostgreSQL 内置的 <strong>物理备份工具</strong>，用于创建数据库集群的 <strong>一致基础备份</strong>。它支持 <strong>在线热备份</strong>（备份时数据库可正常读写），是实现高可用性（如流复制）和灾难恢复的核心工具之一</p>
<h3 id="main函数介绍">main函数介绍</h3>
<p>根据代码分析，<code>main()</code>函数在pg_basebackup.c中主要完成了以下逻辑步骤：</p>
<ol>
<li>
<p><strong>初始化处理</strong>：</p>
<ul>
<li>解析命令行参数</li>
<li>设置信号处理</li>
<li>初始化全局变量和数据结构</li>
</ul>
</li>
<li>
<p><strong>连接数据库</strong>：</p>
<ul>
<li>
<p>建立与PostgreSQL服务器的连接</p>
<pre tabindex="0"><code>conn = GetConnection();
</code></pre></li>
<li>
<p>验证服务器版本兼容性</p>
</li>
</ul>
</li>
<li>
<p><strong>备份准备</strong>：</p>
<ul>
<li>
<p>创建或清理目标目录</p>
<pre tabindex="0"><code>verify_dir_is_empty_or_create(basedir, &amp;made_new_pgdata, &amp;found_existing_pgdata);
</code></pre></li>
<li>
<p>处理表空间映射</p>
</li>
<li>
<p>设置WAL日志目录</p>
</li>
</ul>
</li>
<li>
<p><strong>执行备份</strong>：</p>
<ul>
<li>根据参数选择备份格式(plain/tar)</li>
<li>启动后台WAL流接收进程</li>
<li>通过流复制协议接收基础备份数据</li>
</ul>
<p>关键函数调</p>
<p><code>BaseBackup()</code> - 核心备份逻辑</p>
<p>StartLogStreamer()` - 启动WAL流接收</p>
<p>cleanup_directories_atexit()` - 退出时清理</p>
</li>
</ol>
<h3 id="全量备份">全量备份</h3>
<p>不指定增量备份情况下,默认是全量备份.</p>
<h4 id="主要步骤">主要步骤</h4>
<h5 id="client-端-basebackup">client 端 BaseBackup();</h5>
<p>▶  通过libpq 发送&quot;BASE_BACKUP&quot; 命令给server</p>
<p><img src="https://s2.loli.net/2025/04/29/29WUvoMy6VFPQ5p.png" alt="image-20250429161709054"></p>
<p>▶   对于采用流复制备份单独启动一个进程接收wal日志（数据库在备份期间还存在并发写入的情况）	
StartLogStreamer(xlogstart..)</p>
<ul>
<li>会使用临时物理复制槽位，保证wal在备份期间不丢失</li>
</ul>
<pre tabindex="0"><code>zhou=# select slot_name from pg_replication_slots ;
    slot_name      
---------------------
pg_basebackup_19476
(1 row)
</code></pre><p>▶ 循环接收服务端发送过来的备份数据直到结束
ReceiveArchiveStream(conn, client_compress);</p>
<h5 id="server端-">server端 :</h5>
<h6 id="收到backup请求">收到backup请求</h6>
<p><img src="https://s2.loli.net/2025/04/29/ikfQwVH76Etcaxs.png" alt="image-20250429174951006"></p>
<p>server端的backend 进程.显示的是walsender和pg_basebackup</p>
<pre tabindex="0"><code> 286171 ?        ts     0:00  \_ postgres: walsender postgres 127.0.0.1(46855) sending backup &#34;pg_basebackup base backup&#34;
</code></pre><h6 id="执行具体备份的函数perform_base_backup">执行具体备份的函数perform_base_backup()</h6>
<ol>
<li>
<p>InitializeBackupManifest() 初始化备份清单</p>
</li>
<li>
<p>do_pg_backup_start()  底层备份起始准备函数</p>
<ul>
<li>强制结束当前WAL段文件并开始一个新文件: RequestXLogSwitch(false);</li>
<li>执行检查点RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |(fast ? CHECKPOINT_IMMEDIATE : 0));</li>
</ul>
</li>
<li>
<p>bbsink_begin_backup(sink, &amp;state, SINK_BUFFER_LENGTH); 标记开始备份</p>
</li>
<li>
<p>依次备份每一个表空间</p>
<ul>
<li>
<p>bbsink_begin_archive(sink, &ldquo;base.tar&rdquo;); 准备打包信息，打包的话方便发送</p>
</li>
<li>
<p>backup_label = build_backup_content(backup_state, false);生成备份标签文件(backup_label)</p>
</li>
<li>
<p>sendFileWithContent(sink, BACKUP_LABEL_FILE backup_label, -1, &amp;manifest); backup_label 内容写入 tar 流中</p>
</li>
<li>
<p>把目录内文件发送到客户端 sendDir(sink, &ldquo;.&rdquo;, 1, false, state.tablespaces,  sendtblspclinks, &amp;manifest, InvalidOid, ib);</p>
<p>遇到子目录会再次递归调用自己
<img src="https://s2.loli.net/2025/05/01/spFHyIGrh17K6S4.jpg" alt=""></p>
</li>
</ul>
</li>
<li>
<p>最后发送控制文件 sendFile(sink, XLOG_CONTROL_FILE..)</p>
</li>
</ol>
<h4 id="结束备份-do_pg_backup_stop">结束备份 do_pg_backup_stop（）</h4>
<p><img src="https://s2.loli.net/2025/05/01/yVAIhzZDL3aqCGF.jpg" alt=""></p>
<ul>
<li>wal日志中插入结束备份标志</li>
<li>切换wal日志</li>
<li>写入备份历史文件</li>
</ul>
<h3 id="增量备份postgresql17-新特性">增量备份（Postgresql17 新特性）</h3>
<p>通过incremental_manifest 变量控制逻辑进入到增量备份</p>
<p><img src="https://s2.loli.net/2025/04/29/PIzGcEOgrMTBypm.png" alt="image-20250429143732351"></p>
<h4 id="前提条件">前提条件</h4>
<pre><code>设置参数summarize_wal = on
</code></pre>
<pre tabindex="0"><code>备份时指定 -i 参数
pg_basebackup -h 127.0.0.1 -p 5432 -D /Users/zhou/soft/pgsql/backup/base2 -i base1/backup_manifest
```​        
</code></pre></section>

  <nav
    class="mt-24 flex overflow-hidden rounded-xl bg-black/[3%] text-lg leading-[1.2]! *:flex *:w-1/2 *:items-center *:p-5 *:font-medium *:no-underline dark:bg-white/[8%] [&>*:hover]:bg-black/[2%] dark:[&>*:hover]:bg-white/[3%]"
  ><a class="ltr:pr-3 rtl:pl-3" href="https://gitcode.com/maozicb/database_doc/posts/db/opengauss/opengauss_%E5%90%AF%E5%8A%A8%E9%9B%86%E7%BE%A4%E7%AB%9F%E9%9D%A0%E5%88%A0%E6%96%87%E4%BB%B6/"
      ><span class="ltr:mr-1.5 rtl:ml-1.5">←</span><span>OpenGauss 启动集群竟靠删文件？这是什么「逆向操作」</span></a
    ></nav></article></main><footer
  class="mx-auto flex h-[4.5rem] max-w-(--w) items-center px-8 text-xs tracking-wider uppercase opacity-60"
>
  <div class="mr-auto">&copy;2025
    <a class="link" href="https://gitcode.com/maozicb/database_doc/">数据库的路上</a></div>
</footer>
</body>
</html>
